package com.clei.utils;

import java.util.regex.Pattern;

/**
 * 数字处理工具
 *
 * @author KIyA
 * @date 2020-06-15
 */
public class NumUtil {

    /**
     * 只有一位为1的int数组
     * 可以用于设置某位为1，通过使用位运算OR
     * 也可以用于判断某位是1或0，通过使用位运算AND
     */
    private static final int[] SINGLE_BIT_ONE_ARR;

    /**
     * 只有一位为0的int数组
     * 可以用于设置某位为0，通过使用位运算AND
     * 也可以用于判断某位是1或0，通过使用位运算OR
     */
    private static final int[] SINGLE_BIT_ZERO_ARR;

    // 初始化
    static {
        // bit arr
        int length = 32;
        SINGLE_BIT_ONE_ARR = new int[length];
        SINGLE_BIT_ZERO_ARR = new int[length];
        for (int i = 0; i < length; i++) {
            SINGLE_BIT_ONE_ARR[i] = (1 << i);
            SINGLE_BIT_ZERO_ARR[i] = ~SINGLE_BIT_ONE_ARR[i];
        }
    }

    /**
     * int pattern
     */
    private static final Pattern INT_PATTERN = Pattern.compile("^[-+]?\\d+$");

    /**
     * float pattern
     */
    private static final Pattern FLOAT_PATTERN = Pattern.compile("^[-+]?\\d*.\\d*([eE][-+]?[0-9]+)?$");

    /**
     * number pattern
     */
    private static final Pattern NUM_PATTERN = Pattern.compile("^[-+]?\\d*.?\\d+([eE][-+]?[0-9]+)?$");

    /**
     * 数字进制转换
     *
     * @param str
     * @param from
     * @param to
     * @return
     */
    public static String transRadix(String str, int from, int to) {
        // 校验数据合法性
        if (from < 2 || from > 36 || to < 2 || to > 36) {
            throw new RuntimeException("你是沙雕吗？");
        }
        if (!StringUtil.isDigitOrChar(str)) {
            throw new RuntimeException("你是真滴沙雕！");
        }

        // 获取from进制的num
        int num = Integer.parseInt(str, from);

        // 转成to进制的String
        return Integer.toString(num, to);
    }

    /**
     * 转成二进制字符串，32位长度的
     *
     * @param i i
     * @return binaryString
     */
    public static String toBinaryString32(int i) {
        return StringUtil.complete(Integer.toBinaryString(i), '0', 32);
    }

    /**
     * 转成十六进制字符串，长度8
     *
     * @param i i
     * @return hexString
     */
    public static String toHexString8(int i) {
        return StringUtil.complete(Integer.toHexString(i), '0', 8);
    }

    /**
     * 转成二进制字符串，64位长度的
     *
     * @param l l
     * @return binaryString
     */
    public static String toBinaryString64(long l) {
        return StringUtil.complete(Long.toBinaryString(l), '0', 64);
    }

    /**
     * 转成十六进制字符串，长度16
     *
     * @param l l
     * @return hexString
     */
    public static String toHexString16(long l) {
        return StringUtil.complete(Long.toHexString(l), '0', 16);
    }

    /**
     * num^(i - 1) + ... + num^0
     *
     * @param num
     * @param i
     * @return
     */
    public static int getPowSum(int num, int i) {
        int sum = 0;
        while (i > 0) {
            sum += getPow(num, --i);
        }
        return sum;
    }

    /**
     * num的i次方 num^i
     *
     * @param num
     * @param i
     * @return
     */
    public static int getPow(int num, int i) {
        if (i < 0) {
            throw new RuntimeException("参数错误");
        }
        int result = 1;
        while (i > 0) {
            result *= num;
            i--;
        }
        return result;
    }

    /**
     * 判断一个整数是否是奇数
     *
     * @param num
     * @return
     */
    public static boolean isOdd(int num) {
        return 1 == (1 & num);
    }

    /**
     * 判断一个整数是否是质数/素数
     * 质数只有两个正因数（1和自己）的自然数即为质数
     * 比1大但不是素数的数称为合数
     * 1和0既非素数也非合数。
     *
     * @param num
     * @return
     */
    public static boolean isPrime(int num) {
        if (num < 2) {
            throw new RuntimeException("参数错误");
        }
        int k = (int) Math.sqrt(num) + 1;
        for (int i = 2; i < k; i++) {
            if (0 == num % i) {
                return false;
            }
        }
        return true;
    }

    /**
     * 是否是数字
     *
     * @param str str
     * @return true/false
     */
    public static boolean isNum(String str) {
        return null != str && NUM_PATTERN.matcher(str).matches();
    }

    /**
     * 是否是小数
     *
     * @param str str
     * @return true/false
     */
    public static boolean isFloat(String str) {
        return null != str && FLOAT_PATTERN.matcher(str).matches();
    }

    /**
     * 是否是整数
     *
     * @param str str
     * @return true/false
     */
    public static boolean isInt(String str) {
        return null != str && INT_PATTERN.matcher(str).matches();
    }

    /**
     * 阶乘 n!
     *
     * @param n
     */
    public static int factorial(int n) {
        if (n < 2) {
            return 1;
        }
        if (n > 12) {
            throw new RuntimeException("int类型会溢出");
        }
        int result = 1;
        while (n > 1) {
            result *= n;
            n--;
        }
        return result;
    }

    /**
     * 取得n的第i位的值
     *
     * @param n 源数据
     * @param i bit位置 0到31
     * @return 0或1
     */
    public static int getBit(int n, int i) {
        return 0 == (n & SINGLE_BIT_ONE_ARR[i]) ? 0 : 1;
        // return -1 == (n | SINGLE_BIT_ZERO_ARR[i]) ? 1 : 0;
        // return (n >>> (32 - i - 1)) & 1;
    }

    /**
     * 将n的第i位设置v
     *
     * @param n 源数据
     * @param i bit位置 0到31
     * @param v 要设置的值 0或非0 非0一律当做1
     * @return 修改bit位后的值
     */
    public static int setBit(int n, int i, int v) {
        return 0 == v ? set0(n, i) : set1(n, i);
    }

    /**
     * 将n的第i位设置1
     *
     * @param n 源数据
     * @param i bit位置 0到31
     * @return 修改bit位后的值
     */
    public static int set1(int n, int i) {
        return n | SINGLE_BIT_ONE_ARR[i];
    }

    /**
     * 将n的多位设置1
     *
     * @param n    源数据
     * @param args bit索引数组 0到31
     * @return 修改bit位后的值
     */
    public static int set1(int n, int... args) {
        for (int i : args) {
            n = set1(n, i);
        }
        return n;
    }

    /**
     * 将n的第i位设置0
     *
     * @param n 源数据
     * @param i bit位置 0到31
     * @return 修改bit位后的值
     */
    public static int set0(int n, int i) {
        return n & SINGLE_BIT_ZERO_ARR[i];
    }

    /**
     * 将n的多位设置0
     *
     * @param n    源数据
     * @param args bit索引数组 0到31
     * @return 修改bit位后的值
     */
    public static int set0(int n, int... args) {
        for (int i : args) {
            n = set0(n, i);
        }
        return n;
    }

    public static void main(String[] args) {
        PrintUtil.log(factorial(12));

        PrintUtil.log(getPowSum(3, 4));

        PrintUtil.log(transRadix("x", 36, 10));
    }
}
